﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using Microsoft.Windows.Controls;
using Might.Infrastructure.DOM;
using Might.Infrastructure.Services;
using System.Reflection;
using Might.Modelica.Annotations.Graphics;

namespace Might.UIControls.DomBrowser {
    public class DomBrowserPresenter {
    
        private readonly IDomBrowserView view;

        private readonly Dictionary<IDomNode, TreeViewItem> domNodeToTreeViewItemsMapping =
            new Dictionary<IDomNode, TreeViewItem>();

        private readonly Dictionary<IDomNode, Dictionary<string, TreeViewItem>> nodeGroupsToTreeViewItemsMapping =
            new Dictionary<IDomNode, Dictionary<string, TreeViewItem>>();

        private readonly Dictionary<IDomNode, List<string>> domNodesGroupNames =
            new Dictionary<IDomNode, List<string>>();

        public DomBrowserPresenter(IDomBrowserView view) {
            this.view = view;
            FillViewWithNode(CoreServices.Container.Resolve<IDom>(), null);
        }

        public IDomBrowserView View {
            get { return view; }
        }

        private void FillViewWithNode(IDomNode node, TreeViewItem toItem) {
            
            //create tree view item
            TreeViewItem treeItem = new TreeViewItem();
            treeItem.Header = node.ToString();
            domNodeToTreeViewItemsMapping[node] = treeItem;

            //add to view
            if( toItem == null ) {
                view.AddTreeRoot( treeItem );
            } else {
                view.AddTreeItem( treeItem , toItem );
            }

            createNodeData( node , treeItem );

            //search for children in this node
            string[] groups = node.GetGroupNames();
            IDomNodeCollection col;
            foreach (string group in groups) {
                col = node.GetChildren(group);
                col.CollectionChanged +=
                    DomNodeCollectionChanged;

                FillViewWithGroup(col);
            }

            node.PropertyChanged += NodePropertyChanged;                                                
            
        }

        private void createNodeData( IDomNode node , TreeViewItem toItem ) {

            TreeViewItem treeItem = new TreeViewItem();
            treeItem.Header = "[Data]";
            
            view.AddTreeItem( treeItem , toItem );
            
            List<PropertyInfo> properties = analyzeNode( node );
            
            TreeViewItem dataTreeViewItem;
            string headerStr;
            foreach (PropertyInfo property in properties) { 
            
                dataTreeViewItem = new TreeViewItem();
                headerStr = property.Name + ": ";
                if( property.GetValue( node , null ) == null ) {
                    headerStr += "null";
                } else {
                    headerStr += property.GetValue( node , null ).ToString();
                }
                dataTreeViewItem.Header = headerStr;
                treeItem.Items.Add( dataTreeViewItem );
                
                if( node is Icon && property.Name == "Children" ) {                    
                    Icon icon = node as Icon;
                    foreach( IDomNode iconNode in icon.Children ) {
                        FillViewWithNode( iconNode , dataTreeViewItem );
                    }                                                                    
                }
                
            }

        }

        private List<PropertyInfo> analyzeNode( IDomNode node ) {
            Type[] interfaces = node.GetType().GetInterfaces();            

            var interfacesProperties = new List<PropertyInfo>();
            foreach( Type t in interfaces ) {
                interfacesProperties.AddRange( t.GetProperties() );
            }                    
            
            var labels = new List<string>();
            foreach( PropertyInfo propInfo in interfacesProperties ) {
                labels.Add( propInfo.Name );
            }

            foreach( PropertyInfo propInfo in node.GetType().GetProperties() ) {                            
                if( !labels.Contains( propInfo.Name ) ) {
                    labels.Add( propInfo.Name );
                    interfacesProperties.Add( propInfo );
                }                
            }

            interfacesProperties.Sort( ComparePropertyByName );           

            return interfacesProperties;
                        
        }

       private static int ComparePropertyByName( PropertyInfo info1 , PropertyInfo info2 ) {
            if( info1 == null ) {
                if( info2 == null ) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if( info2 == null ) {
                    return 1;
                } else {
                    return info1.Name.CompareTo( info2.Name );
                }
            }            
        }

        private void FillViewWithGroup(IDomNodeCollection col) {
            if (!domNodesGroupNames.ContainsKey(col.Owner)) {
                domNodesGroupNames[col.Owner] = new List<string>();
            }
            domNodesGroupNames[col.Owner].Add(col.GroupName);

            TreeViewItem treeItem = new TreeViewItem {Header = "[Group] " + col.GroupName};

            view.AddTreeItem(treeItem, domNodeToTreeViewItemsMapping[col.Owner]);

            if( !nodeGroupsToTreeViewItemsMapping.ContainsKey( col.Owner ) ) {
                nodeGroupsToTreeViewItemsMapping[col.Owner] = new Dictionary<string , TreeViewItem>();
            }
            nodeGroupsToTreeViewItemsMapping[col.Owner][col.GroupName] = treeItem;

            foreach (IDomNode node in col) {                
                FillViewWithNode(node, treeItem);
            }
        }

        private void NodePropertyChanged(object sender, PropertyChangedEventArgs e) {
            if (e.PropertyName == "Children") {
                if (sender is IDomNode) {
                    IDomNode node = sender as IDomNode;

                    string[] groups = node.GetGroupNames();

                    List<string> oldGroups;
                    if (domNodesGroupNames.ContainsKey(node)) {
                        oldGroups = domNodesGroupNames[node];
                    } else {
                        oldGroups = new List<string>();
                    }

                    foreach (string group in groups) {
                        if (!oldGroups.Contains(group)) {
                            FillViewWithGroup(node.GetChildren(group));
                        }
                    }
                } else {
                    throw new ArgumentException("Sender should be IDomNode");
                }
            }
        }

        private void DomNodeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
            if (e.Action == NotifyCollectionChangedAction.Add) {
                if (sender is IDomNodeCollection) {
                    IDomNodeCollection col = sender as IDomNodeCollection;
                    foreach( IDomNode node in e.NewItems ) {
                        FillViewWithNode( node , nodeGroupsToTreeViewItemsMapping[col.Owner][col.GroupName] );                        
                    }
                } else {
                    throw new ArgumentException("Sender should be IDomNodeCollection");
                }
            } else {
                throw new NotImplementedException();
            }
        }
    }
}
